package timing.ukulele.flow.controller;

import timing.ukulele.flow.service.CustomTaskRuntime;
import timing.ukulele.flow.util.ActivitiUtil;
import timing.ukulele.flow.util.ProcessImageManager;
import timing.ukulele.flow.vo.MyTask;
import timing.ukulele.flow.vo.ProcessInstanceVO;
import timing.ukulele.common.ResponseResult;
import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.util.SimpleContext;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.activiti.api.process.model.ProcessDefinition;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.bpmn.model.*;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 流程相关接口
 */@RestController
@RequestMapping("/process")
@Api(tags = "流程相关接口")
@Slf4j
public class ProcessController {

    /**
     * 流程定义相关操作
     */
    private final ProcessRuntime processRuntime;


    private final CustomTaskRuntime customTaskRuntime;

    private final RepositoryService repositoryService;

    private final TaskService taskService;

    private final HistoryService historyService;

    private final RuntimeService runtimeService;

    private final ProcessImageManager processImageManager;

    public ProcessController(ProcessRuntime processRuntime, CustomTaskRuntime customTaskRuntime, RepositoryService repositoryService, TaskService taskService,
                             HistoryService historyService, RuntimeService runtimeService, ProcessImageManager processImageManager) {
        this.processRuntime = processRuntime;
        this.customTaskRuntime = customTaskRuntime;
        this.repositoryService = repositoryService;
        this.taskService = taskService;
        this.historyService = historyService;
        this.runtimeService = runtimeService;
        this.processImageManager = processImageManager;
    }

    @GetMapping("/getProcessInstances/{tenantId}")
    @ApiOperation("根据系统id,查询未删除的流程实例情况")
    public ResponseResult getProcessInstances(@PathVariable("tenantId") String tenantId,
                                              @ApiParam(value = "当前页码", example = "1") @RequestParam("current") Integer current,
                                              @ApiParam(value = "每页大小", example = "10") @RequestParam("pageSize") Integer pageSize,
                                              @RequestParam(required = false) String searchName,
                                              @RequestParam(required = false) String id,
                                              @RequestParam(required = false) String startTime,
                                              @RequestParam(required = false) String endTime,
                                              @RequestParam(required = false) String loginname,
                                              @RequestParam(required = false) String varname,
                                              @RequestParam(required = false) String varvalue,
                                              @RequestParam(required = false) Integer isdone
    ) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<ProcessInstanceVO> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(current, pageSize);
        //存放流程实例的集合
        List list = new ArrayList<>();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");

        try {
            //创建已生成的流程实例查询对象
            HistoricProcessInstanceQuery historicProcessInstanceQuery =
                    historyService.createHistoricProcessInstanceQuery();
            //  List<HistoricProcessInstance> historicProcessInstanceList =historicProcessInstanceQuery.processInstanceTenantId(tenantId).notDeleted().listPage((current - 1) * pageSize, pageSize);
            if (!StringUtils.isEmpty(searchName)) {
                historicProcessInstanceQuery = historicProcessInstanceQuery.processDefinitionName(searchName);
            }
            if (!StringUtils.isEmpty(tenantId)) {
                historicProcessInstanceQuery = historicProcessInstanceQuery.processInstanceTenantId(tenantId);
            }
            if (!StringUtils.isEmpty(id)) {
                historicProcessInstanceQuery = historicProcessInstanceQuery.processInstanceId(id);
            }
            if (!StringUtils.isEmpty(startTime)) {
                historicProcessInstanceQuery = historicProcessInstanceQuery.startedAfter(simpleDateFormat.parse(startTime));
            }
            if (!StringUtils.isEmpty(endTime)) {

                Date dend = simpleDateFormat.parse(endTime);
                dend = addDay(dend, 1);

                historicProcessInstanceQuery = historicProcessInstanceQuery.startedBefore(dend);
            }
            if (!StringUtils.isEmpty(loginname))
                historicProcessInstanceQuery = historicProcessInstanceQuery.involvedUser(loginname);

            if ((!StringUtils.isEmpty(varname)) && (!StringUtils.isEmpty(varvalue)))
                historicProcessInstanceQuery = historicProcessInstanceQuery.variableValueLike(varname, varvalue);

            if (isdone != null) {
                int idone = isdone.intValue();
                if (idone == 1)
                    historicProcessInstanceQuery = historicProcessInstanceQuery.finished();
                if (idone == 0)
                    historicProcessInstanceQuery = historicProcessInstanceQuery.unfinished();
            }
            List<HistoricProcessInstance> historicProcessInstanceList = historicProcessInstanceQuery.notDeleted().listPage((current - 1) * pageSize, pageSize);

            for (HistoricProcessInstance historicProcessInstance : historicProcessInstanceList) {
                ProcessInstanceVO processInstanceVO = new ProcessInstanceVO();
                BeanUtils.copyProperties(historicProcessInstance, processInstanceVO);
                if (processInstanceVO.getEndTime() != null) {
                    processInstanceVO.setStatus("已完成");
                } else {
                    processInstanceVO.setStatus("未完成");
                }
                list.add(processInstanceVO);
            }
            page.setRecords(list);
            page.setTotal(historyService.createHistoricProcessInstanceQuery().processInstanceTenantId(tenantId).notDeleted().count());
        } catch (Exception ex) {
            log.error(ex.getMessage());
            return ResponseResult.error("查询流程实例失败!");
        }
        return ResponseResult.success(page);
    }

    @DeleteMapping("/deleteProcessInstance/{processInstanceId}")
    @ApiOperation("删除流程实例")
    public ResponseResult deleteProcessInstance(@PathVariable(value = "processInstanceId") String processInstanceId,
                                                @ApiParam(value = "删除原因", required = true) @RequestParam("reason") String reason) {
        try {
            log.info("=============删除流程实例============");
            List<org.activiti.engine.runtime.ProcessInstance> processInstances =
                    runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).list();
            if (processInstances.isEmpty()) {
                // 历史流程实例
                List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
                        .processInstanceId(processInstanceId).list();
                if (historicProcessInstances.isEmpty()) {
                    String msg = "No process instances with the ID: " + processInstanceId;
                    log.error(msg);
                    throw new ActivitiException(msg);
                }
                historyService.deleteHistoricProcessInstance(processInstanceId);
                return ResponseResult.success("删除流程实例成功！");
            }
            runtimeService.deleteProcessInstance(processInstanceId, reason);
            return ResponseResult.success("删除流程实例成功！");
        } catch (ActivitiException e) {
            return ResponseResult.error("删除流程实例失败！");
        }
    }

    //获取办事人员
    @GetMapping("/getProcessInstanceId/{processInstanceId}")
    public ResponseResult getProcessInstanceId(
            @PathVariable(value = "processInstanceId") String processInstanceId,
            @ApiParam("租户id") @RequestParam("tenantId") String tenantId
    ) {
        try {
            String assignee = "";
            System.out.print("processInstanceId:" + processInstanceId);
            //获取当前流程
            Task nextTask = taskService
                    .createTaskQuery().processInstanceId(processInstanceId).singleResult();
            if (nextTask != null) {
                assignee = nextTask.getAssignee();
            }
            return ResponseResult.success(assignee);
        } catch (ActivitiException e) {
            return ResponseResult.error("修改办事人员失败！");
        }
    }

    //修改办事人员
    @GetMapping("/updateProcessInstanceId/{processInstanceId}")
    public ResponseResult updateProcessInstanceId(
            @PathVariable(value = "processInstanceId") String processInstanceId,
            @ApiParam(value = "修改的人员", required = true) @RequestParam("UpdatePersion") String UpdatePersion,
            @ApiParam("租户id") @RequestParam("tenantId") String tenantId
    ) {
        try {
            String updateAssignee = "";
            System.out.print("UpdatePersion:" + UpdatePersion);
            System.out.print("processInstanceId:" + processInstanceId);
            System.out.print("tenantId:" + tenantId);
//创建任务查询对象
            List<Task> taskList;
            TaskQuery taskQuery;
            return ResponseResult.success("");
        } catch (ActivitiException e) {
            return ResponseResult.error("修改办事人员失败！");
        }
    }

    //批量删除
    @DeleteMapping("/deleteAllProcessInstance/{processInstanceId}")
    @ApiOperation("删除流程实例")
    public ResponseResult deleteAllProcessInstance(
            @PathVariable(value = "processInstanceId") String processInstanceId
    ) {
        try {
            String[] processIds = processInstanceId.split(",");
            log.info("=============批量删除流程实例============");
            if (processIds.length > 0) {
                for (int i = 0; i < processIds.length; i++) {
                    String processId = processIds[i];
                    List<org.activiti.engine.runtime.ProcessInstance> processInstances =
                            runtimeService.createProcessInstanceQuery().processInstanceId(processId).list();
                    if (processInstances.isEmpty()) {
                        // 历史流程实例
                        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
                                .processInstanceId(processId).list();
                        if (historicProcessInstances.isEmpty()) {
                            String msg = "No process instances with the ID: " + processId;
                            log.error(msg);
                            throw new ActivitiException(msg);
                        }
                        historyService.deleteHistoricProcessInstance(processId);
                        return ResponseResult.success("删除流程实例成功！");
                    }
                    runtimeService.deleteProcessInstance(processInstanceId, "批量删除");

                }

            }
            return ResponseResult.success("删除流程实例成功！");
        } catch (ActivitiException e) {
            return ResponseResult.error("删除流程实例失败！");
        }
    }

    @GetMapping("/getImageByProcessInstanceId/{processInstanceId}")
    @ApiOperation("根据流程实例id查看实时流程图")
    public void getImageByProcessInstanceId(HttpServletResponse response,
                                            @ApiParam(value = "流程实例id", required = true) @PathVariable("processInstanceId") String processInstanceId,
                                            @ApiParam("租户id") @RequestParam("tenantId") String tenantId) {
        try {
            InputStream is = processImageManager.getFlowImgByProcInstId(processInstanceId, tenantId);
            if (is == null) {
                return;
            }
            byte[] b = new byte[1024];
            int len = -1;
            while ((len = is.read(b, 0, 1024)) != -1) {
                response.getOutputStream().write(b, 0, len);
            }
            is.close();
        } catch (Exception ex) {
            log.error(ex.getMessage());
            return;
        }
        return;
    }

    @PostMapping(path = "/printProcessess")
    @ApiOperation(value = "根据用户名打印所有流程定义", notes = "根据用户名打印所有流程定义")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名", dataType = "String", paramType = "query", example = "")
    })
    public ResponseResult printProcessess(@RequestParam("username") String username) {
        try {
            Page processDefinitionPage = ActivitiUtil.getProcessDefinitionList(0, 10);
            // getTotalItems()取得的是包含所有版本的总数
            // getContent().size()取得的是流程数，多个版本的同一流程只算一次
            log.info("流程定义数: " +
                    processDefinitionPage.getContent().size());
            for (Object pd : processDefinitionPage.getContent()) {
                log.info("\t > 流程定义: " + pd);
            }
            return ResponseResult.success("打印流程成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error("打印流程失败");
    }

    @PostMapping(path = "/findProcessDefinitionList")
    @ApiOperation(value = "获取所有流程定义", notes = "获取所有流程定义")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "startNum", value = "分页开始下标", dataType = "Integer", paramType = "query"),
            @ApiImplicitParam(name = "endNum", value = "分页结束下标", dataType = "Integer", paramType = "query")
    })
    public ResponseResult findProcessDefinitionList(@RequestParam("startNum") Integer startNum, @RequestParam("endNum") Integer endNum) {
        try {
            Page<ProcessDefinition> page = processRuntime
                    .processDefinitions(Pageable.of(startNum, endNum));
            return ResponseResult.success(page);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error("获取所有流程定义失败");
    }

    @PostMapping(path = "/startProcessInstance")
    @ApiOperation(value = "根据用户名流程实例启动", notes = "根据用户名流程实例启动")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processKey", value = "流程Key => 对应bpmn文件里的id", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processName", value = "流程实例名", dataType = "String", paramType = "query", example = "")
    })
    public ResponseResult startProcessInstance(@RequestParam("username") String username, @RequestParam("processKey") String processKey, @RequestParam("processName") String processName) {
        try {
            ActivitiUtil.startProcessInstance(username, processKey, processName);
            return ResponseResult.success("流程实例启动成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error("流程实例启动失败");
    }

    @PostMapping(path = "/startProcessInstanceWithVariables")
    @ApiOperation(value = "根据用户名及map变量流程实例启动", notes = "根据用户名及map变量流程实例启动")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processKey", value = "流程Key => 对应bpmn文件里的id", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processName", value = "流程实例名", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "variables", value = "map变量variables", dataType = "Map", paramType = "query")
    })
    public ResponseResult startProcessInstanceWithVariables(@RequestParam("username") String username,
                                                            @RequestParam("processKey") String processKey,
                                                            @RequestParam("processName") String processName,
                                                            @RequestParam("variables") HashMap<String, Object> variables) {
        try {
            ActivitiUtil.startProcessInstanceWithVariables(username, processKey, processName, variables);
            return ResponseResult.success("流程实例启动成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error("流程实例启动失败");
    }

    @PostMapping(path = "/startProcessInstanceWithBusinessKey")
    @ApiOperation(value = "根据用户名及businessKey流程实例启动", notes = "根据用户名及businessKey流程实例启动")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processKey", value = "流程Key => 对应bpmn文件里的id", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processName", value = "流程实例名", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "businessKey", value = "businessKey", dataType = "String", paramType = "query", example = "")
    })
    public ResponseResult startProcessInstanceWithBusinessKey(@RequestParam("username") String username,
                                                              @RequestParam("processKey") String processKey,
                                                              @RequestParam("processName") String processName,
                                                              @RequestParam("businessKey") String businessKey) {
        try {
            ProcessInstance processInstance = ActivitiUtil.startProcessInstance(username, processKey, processName, businessKey);
            return ResponseResult.success(processInstance);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error("流程实例启动失败");
    }

    @PostMapping(path = "/startProcessInstanceWithVariablesAndBusinessKey")
    @ApiOperation(value = "根据用户名及businessKey及map变量流程实例启动", notes = "根据用户名及businessKey及map变量流程实例启动")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processKey", value = "流程Key => 对应bpmn文件里的id", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processName", value = "流程实例名", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "businessKey", value = "businessKey", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "variables", value = "map变量variables", dataType = "Map", paramType = "query")
    })
    public ResponseResult startProcessInstanceWithVariablesAndBusinessKey(@RequestParam("username") String username,
                                                                          @RequestParam("processKey") String processKey,
                                                                          @RequestParam("processName") String processName,
                                                                          @RequestParam("businessKey") String businessKey,
                                                                          @RequestParam("variables") HashMap<String, Object> variables) {
        try {
            ActivitiUtil.startProcessInstance(username, processKey, processName, businessKey, variables);
            return ResponseResult.success("流程实例启动成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error("流程实例启动失败");
    }

    @PostMapping(path = "/getVariableValue")
    @ApiOperation(value = "取得流程变量的值", notes = "取得流程变量的值")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "variableName", value = "变量名", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processInstanceId", value = "流程实例Id", dataType = "String", paramType = "query", example = "")
    })
    public ResponseResult getVariableValue(@RequestParam("variableName") String variableName,
                                           @RequestParam("processInstanceId") String processInstanceId) {
        try {
            String string = ActivitiUtil.getVariableValue(variableName, processInstanceId);
            return ResponseResult.success(string);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error("取得流程变量的值失败");
    }

    @PostMapping(path = "/getFlowNode")
    @ApiOperation(value = "根据活动节点和流程定义Id获取该活动节点的组件信息", notes = "根据活动节点和流程定义Id获取该活动节点的组件信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "processDefinitionId", value = "流程定义Id", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "flowElementId", value = "活动节点Id", dataType = "String", paramType = "query", example = "")
    })
    public ResponseResult getFlowNode(@RequestParam("processDefinitionId") String processDefinitionId,
                                      @RequestParam("flowElementId") String flowElementId) {
        try {
            FlowNode flowNode = ActivitiUtil.getFlowNode(processDefinitionId, flowElementId);
            return ResponseResult.success(flowNode);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error("取该活动节点的组件信息失败");
    }


    /**
     * 根据key和value判断el表达式是否通过
     *
     * @param key   el表达式key
     * @param el    el表达式
     * @param value el表达式传入值
     * @return
     */
    public boolean isCondition(String key, String el, String value) {
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        context.setVariable(key, factory.createValueExpression(value, String.class));
        ValueExpression e = factory.createValueExpression(context, el, boolean.class);
        return (Boolean) e.getValue(context);
    }

    /**
     * 根据el表达式取得满足条件的下一个activityId
     *
     * @param executionId
     * @param processInstanceId
     * @param outgoingFlows
     * @return
     */
    public String getNextActivityId(String executionId,
                                    String processInstanceId,
                                    List<SequenceFlow> outgoingFlows) {
        String activityId = null;
        // 遍历出线
        for (SequenceFlow outgoingFlow : outgoingFlows) {
            // 取得线上的条件
            String conditionExpression = outgoingFlow.getConditionExpression();
            // 取得所有变量
            Map<String, Object> variables = runtimeService.getVariables(executionId);
            String variableName = "";
            // 判断网关条件里是否包含变量名
            for (String s : variables.keySet()) {
                if (conditionExpression.contains(s)) {
                    // 找到网关条件里的变量名
                    variableName = s;
                }
            }
            String conditionVal = getPublicVariableValue(variableName, processInstanceId);
            // 判断el表达式是否成立
            if (isCondition(variableName, conditionExpression, conditionVal)) {
                // 取得目标节点
                FlowElement targetFlowElement = outgoingFlow.getTargetFlowElement();
                activityId = targetFlowElement.getId();
            }
        }
        return activityId;
    }

    /**
     * 获取已经流转的线
     *
     * @param bpmnModel
     * @param historicActivityInstances
     * @return
     */
    private List<String> getHighLightedFlows(BpmnModel bpmnModel,
                                             List<HistoricActivityInstance> historicActivityInstances) {
        // 高亮流程已发生流转的线id集合
        List<String> highLightedFlowIds = new ArrayList<>();
        // 全部活动节点
        List<FlowNode> historicActivityNodes = new ArrayList<>();
        // 已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess()
                    .getFlowElement(historicActivityInstance.getActivityId(), true);
            historicActivityNodes.add(flowNode);
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstances.add(historicActivityInstance);
            }
        }

        FlowNode currentFlowNode = null;
        FlowNode targetFlowNode = null;
        // 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess()
                    .getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();


            // 遍历outgoingFlows并找到已已流转的 满足如下条件认为已已流转：
            // 1.当前节点是并行网关或兼容网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
            // 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最早的流转节点视为有效流转
            if ("parallelGateway".equals(currentActivityInstance.getActivityType())
                    || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
                // 遍历历史活动节点，找到匹配流程目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess()
                            .getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicActivityNodes.contains(targetFlowNode)) {
                        highLightedFlowIds.add(targetFlowNode.getId());
                    }
                }
            } else {
                List<Map<String, Object>> tempMapList = new ArrayList<>();
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                            Map<String, Object> map = new HashMap<>(16);
                            map.put("highLightedFlowId", sequenceFlow.getId());
                            map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
                            tempMapList.add(map);
                        }
                    }
                }

                if (!CollectionUtils.isEmpty(tempMapList)) {
                    // 遍历匹配的集合，取得开始时间最早的一个
                    long earliestStamp = 0L;
                    String highLightedFlowId = null;
                    for (Map<String, Object> map : tempMapList) {
                        long highLightedFlowStartTime = Long.parseLong(map.get("highLightedFlowStartTime").toString());
                        if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {
                            highLightedFlowId = map.get("highLightedFlowId").toString();
                            earliestStamp = highLightedFlowStartTime;
                        }
                    }

                    highLightedFlowIds.add(highLightedFlowId);
                }

            }

        }
        return highLightedFlowIds;
    }

    /**
     * 根据流程实例Id,获取实时流程图片
     *
     * @param processInstanceId
     * @param outputStream
     * @return
     */
    public void getFlowImgByInstanceId(String processInstanceId, OutputStream outputStream) {
        try {
            if (StringUtils.isEmpty(processInstanceId)) {
                log.error("processInstanceId is null");
                return;
            }
            // 获取历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService
                    .createHistoricProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult();
            // 获取流程中已经执行的节点，按照执行先后顺序排序
            List<HistoricActivityInstance> historicActivityInstances = historyService
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .orderByHistoricActivityInstanceId()
                    .asc().list();
            // 高亮已经执行流程节点ID集合
            List<String> highLightedActivitiIds = new ArrayList<>();
            int index = 1;
            for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                // 用默认颜色
                highLightedActivitiIds.add(historicActivityInstance.getActivityId());
                index++;
            }

            ProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();

            BpmnModel bpmnModel = repositoryService
                    .getBpmnModel(historicProcessInstance.getProcessDefinitionId());
            // 高亮流程已发生流转的线id集合
            List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstances);

            // 使用默认配置获得流程图表生成器，并生成追踪图片字符流
            InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel,
                    highLightedActivitiIds, highLightedFlowIds, "宋体",
                    "微软雅黑", "黑体");

            // 输出图片内容
            int byteSize = 1024;
            byte[] b = new byte[byteSize];
            int len;
            while ((len = imageStream.read(b, 0, byteSize)) != -1) {
                outputStream.write(b, 0, len);
            }
        } catch (Exception e) {
            log.error("processInstanceId" + processInstanceId + "生成流程图失败，原因：" + e.getMessage(), e);
        }

    }

    @PostMapping(path = "/getAllFlowElements")
    @ApiOperation(value = "获得流程所有元素", notes = "获得流程所有元素")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "processDefinitionId", value = "流程定义Id", dataType = "String", paramType = "query", example = ""),
            @ApiImplicitParam(name = "processInstanceId", value = "流程实例Id", dataType = "String", paramType = "query", example = "")
    })
    public ResponseResult getAllFlowElements(@RequestParam("processDefinitionId") String processDefinitionId,
                                             @RequestParam("processInstanceId") String processInstanceId) {
        try {
            List<MyTask> list = ActivitiUtil.getAllFlowElements(processDefinitionId, processInstanceId);
            return ResponseResult.success(list);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.error("取该活动节点的组件信息失败");
    }

    /**
     * 取得当前节点的状态（已完成/进行中/未开始）
     *
     * @param flowElement
     * @return
     */
    public String getFlowElementStatus(FlowElement flowElement, String executionId) {
        Activity activity = (Activity) flowElement;
        MultiInstanceLoopCharacteristics loopCharacteristics = activity.getLoopCharacteristics();
        if (loopCharacteristics == null) {
            // 节点为单实例的场合
            UserTask userTask = (UserTask) flowElement;
            //            getTaskStatus();
        } else {
            // 节点为多实例的场合
            // 获取完成任务实例数量
            Integer nrOfCompletedInstances = (Integer) runtimeService.getVariable(executionId, "nrOfCompletedInstances");
            // 获取会签总实例数量
            Integer numberOfInstances = (Integer) runtimeService.getVariable(executionId, "numberOfInstances");

            if (nrOfCompletedInstances.equals(numberOfInstances)) {
                // 已完成
            } else {
                // 进行中
            }
        }
        return null;
    }

    /**
     * 通过表达式获取其中的变量名
     *
     * @param expression 表达式
     * @return 变量名
     */
    public String getVariableNameByExpression(String expression) {
        return expression.replace("${", "")
                .replace("}", "");
    }

    /**
     * 取得流程变量的值
     *
     * @param variableName      变量名
     * @param processInstanceId 流程实例Id
     * @return
     */
    public String getPublicVariableValue(String variableName, String processInstanceId) {
        Execution execution = runtimeService
                .createExecutionQuery().processInstanceId(processInstanceId).list().get(0);
        Object object = runtimeService.getVariable(execution.getId(), variableName);
        return object == null ? "" : object.toString();
    }

    public static Date addDay(Date curdate, int days) {
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(curdate);
        calendar.add(calendar.DATE, days);//
        return calendar.getTime();
    }

}
